/* SPDX-License-Identifier: GPL-2.0 */
#ifndef KERNEL_TIME_TIMEKEEPING_H
#define KERNEL_TIME_TIMEKEEPING_H

#include <seminix/time.h>
#include <seminix/seqlock.h>
#include <seminix/timekeeping.h>
#include <devices/clocksource.h>

/**
 * struct tk_read_base - base structure for timekeeping readout
 * @clock:	Current clocksource used for timekeeping.
 * @mask:	Bitmask for two's complement subtraction of non 64bit clocks
 * @cycle_last: @clock cycle value at last update
 * @mult:	(NTP adjusted) multiplier for scaled math conversion
 * @shift:	Shift value for scaled math conversion
 * @ktime_nsec: Shifted (fractional) nano seconds offset for readout
 *
 * This struct has size 56 byte on 64 bit. Together with a seqcount it
 * occupies a single 64byte cache line.
 *
 * The struct is separate from struct timekeeper as it is also used
 * for a fast NMI safe accessors.
 *
 * @base_real is for the fast NMI safe accessor to allow reading clock
 * realtime from any context.
 */
struct tk_read_base {
    struct clocksource	*clock;
    u64			mask;
    u64			cycle_last;
    u32			mult;
    u32			shift;
    u64			ktime_nsec;
};

/**
 * struct timekeeper - Structure holding internal timekeeping values.
 * @tkr_mono:		The readout base structure for CLOCK_MONOTONIC
 * @offs_real:		Offset clock monotonic -> clock realtime
 *
 * Note: For timespec(64) based interfaces wall_to_monotonic is what
 * we need to add to xtime (or xtime corrected for sub jiffie times)
 * to get to monotonic time.  Monotonic is pegged at zero at system
 * boot time, so wall_to_monotonic will be negative, however, we will
 * ALWAYS keep the tv_nsec part positive so we can use the usual
 * normalization.
 *
 * wall_to_monotonic is moved after resume from suspend for the
 * monotonic time not to jump. We need to add total_sleep_time to
 * wall_to_monotonic to get the real boot based time offset.
 *
 * wall_to_monotonic is no longer the boot time, getboottime must be
 * used instead.
 */
struct timekeeper {
    struct tk_read_base	tkr_mono;
    ktime_t		offs_real;
    u64			ktime_sec;
};

static inline u64 clocksource_delta(u64 now, u64 last, u64 mask)
{
    return (now - last) & mask;
}

int timekeeping_valid_for_hres(void);

void update_wall_time(void);

/*
 * Internal interfaces for kernel/time/
 */
extern ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq, ktime_t *offs_real);

#endif /* !KERNEL_TIME_TIMEKEEPING_H */
